﻿using System;
using System.Collections.Generic;
using System.Text;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Controllers;
using Microsoft.Xna.Framework;

namespace FarseerGames.FarseerPhysics.Controllers
{
    /// <summary>
    /// Controller that performs explosions on objects
    /// </summary>
    public class ExplosionController:Controller
    {
        private List<ExplotionParameters> explotions;
        private PhysicsSimulator physicsSimulator;

        public int CollisionGroup { get; set; }

        public ExplotionEventHandler OnExplotion;

        public delegate void ExplotionEventHandler(Object sourceTag, Geom affectedGeom);

        public void AddExplotion(float x, float y, float strength, float radius, Object tag)
        {
            AddExplotion(new Vector2(x, y), strength, radius, tag);
        }

        public void AddExplotion( Vector2 origin, float strength, float radius, Object tag)
        {
            ExplotionParameters ex = new ExplotionParameters();
            ex.origin = origin;
            ex.strength = strength;
            ex.effectRadius = radius;
            ex.tag = tag;
            explotions.Add(ex);
        }

        public ExplosionController(PhysicsSimulator physicsSimulator)
        {
            explotions = new List<ExplotionParameters>();
            this.physicsSimulator = physicsSimulator;
            this.physicsSimulator.Add(this);
            CollisionGroup = 0;
        }

        public override void Validate()
        {
        }

        public override void Update(float dt)
        {
            if (!Enabled) return;
            if (IsDisposed) return;
            if (explotions.Count <= 0) return;

            foreach (Geom geom in physicsSimulator.GeomList)
            {
                Body body = geom.Body;
                if (body.IsStatic) continue; // do not apply to static bodies
                if (body.Enabled == false) continue; // do not apply to disabled bodies
                if (CollisionGroup != 0 && CollisionGroup == geom.CollisionGroup) continue; //if collision group is set do not affect objects in same group
                foreach (ExplotionParameters explotion in explotions)
                {
                    Vector2 direction = body.Position - explotion.origin;
                    float distance = direction.Length();
                    if (distance < explotion.effectRadius)
                    {
                        //apply strength based on distance, but never below strength/10 or above strength
                        float strength = MathHelper.Clamp(explotion.strength * explotion.effectRadius / distance, explotion.strength / 10.0f, explotion.strength);
                        direction.Normalize();
                        body.ApplyImpulse(direction * strength);

                        //notify listener
                        if (OnExplotion != null)
                        {
                            OnExplotion(explotion.tag, geom);
                        }
                    }
                }
            
            }
            explotions.Clear(); //only explode once
        }


        protected class ExplotionParameters
        {
            public float strength;
            public Vector2 origin;
            public float effectRadius;
            public Object tag;
        }
    }


}
